@@ -37,6 +37,7 @@ gem 'haversine' |
||
37 | 37 |
# Optional Services. |
38 | 38 |
gem 'omniauth-37signals' # BasecampAgent |
39 | 39 |
# gem 'omniauth-github' |
40 |
+gem 'omniauth-wunderlist', github: 'wunderlist/omniauth-wunderlist', ref: 'd0910d0396107b9302aa1bc50e74bb140990ccb8' |
|
40 | 41 |
|
41 | 42 |
# Bundler <1.5 does not recognize :x64_mingw as a valid platform name. |
42 | 43 |
# Unfortunately, it can't self-update because it errors when encountering :x64_mingw. |
@@ -8,6 +8,15 @@ GIT |
||
8 | 8 |
http_parser.rb (~> 0.6.0) |
9 | 9 |
simple_oauth (~> 0.2.0) |
10 | 10 |
|
11 |
+GIT |
|
12 |
+ remote: git://github.com/wunderlist/omniauth-wunderlist.git |
|
13 |
+ revision: d0910d0396107b9302aa1bc50e74bb140990ccb8 |
|
14 |
+ ref: d0910d0396107b9302aa1bc50e74bb140990ccb8 |
|
15 |
+ specs: |
|
16 |
+ omniauth-wunderlist (0.0.1) |
|
17 |
+ omniauth (~> 1.0) |
|
18 |
+ omniauth-oauth2 (~> 1.1) |
|
19 |
+ |
|
11 | 20 |
GEM |
12 | 21 |
remote: https://rubygems.org/ |
13 | 22 |
specs: |
@@ -523,6 +532,7 @@ DEPENDENCIES |
||
523 | 532 |
omniauth-dropbox |
524 | 533 |
omniauth-tumblr |
525 | 534 |
omniauth-twitter |
535 |
+ omniauth-wunderlist! |
|
526 | 536 |
pg |
527 | 537 |
protected_attributes (~> 1.0.8) |
528 | 538 |
pry |
@@ -252,8 +252,8 @@ h2 .scenario, a span.label.scenario { |
||
252 | 252 |
width: 200px; |
253 | 253 |
} |
254 | 254 |
|
255 |
-$services: twitter 37signals github tumblr dropbox; |
|
256 |
-$service-colors: #55acee #8fc857 #444444 #2c4762 #007EE5; |
|
255 |
+$services: twitter 37signals github tumblr dropbox wunderlist; |
|
256 |
+$service-colors: #55acee #8fc857 #444444 #2c4762 #007EE5 #ED5F27; |
|
257 | 257 |
|
258 | 258 |
@mixin services { |
259 | 259 |
@each $service in $services { |
@@ -57,6 +57,8 @@ module ApplicationHelper |
||
57 | 57 |
case provider.to_sym |
58 | 58 |
when :twitter, :tumblr, :github, :dropbox |
59 | 59 |
icon_tag("fa-#{provider}") |
60 |
+ when :wunderlist |
|
61 |
+ icon_tag("fa-list") |
|
60 | 62 |
else |
61 | 63 |
icon_tag("fa-lock") |
62 | 64 |
end |
@@ -0,0 +1,78 @@ |
||
1 |
+module Agents |
|
2 |
+ class WunderlistAgent < Agent |
|
3 |
+ include FormConfigurable |
|
4 |
+ include Oauthable |
|
5 |
+ valid_oauth_providers :wunderlist |
|
6 |
+ |
|
7 |
+ cannot_be_scheduled! |
|
8 |
+ |
|
9 |
+ gem_dependency_check { Devise.omniauth_providers.include?(:wunderlist) } |
|
10 |
+ |
|
11 |
+ description <<-MD |
|
12 |
+ #{'## Include the `omniauth-wunderlist` gem in your `Gemfile` and set `WUNDERLIST_OAUTH_KEY` and `WUNDERLIST_OAUTH_SECRET` in your environment to use this Agent' if dependencies_missing?} |
|
13 |
+ |
|
14 |
+ The WunderlistAgent creates new new tasks based on the incoming event. |
|
15 |
+ |
|
16 |
+ To be able to use this Agent you need to authenticate with Wunderlist in the [Services](/services) section first. |
|
17 |
+ |
|
18 |
+ MD |
|
19 |
+ |
|
20 |
+ def default_options |
|
21 |
+ { |
|
22 |
+ 'list_id' => '', |
|
23 |
+ 'title' => '{{title}}' |
|
24 |
+ } |
|
25 |
+ end |
|
26 |
+ |
|
27 |
+ form_configurable :list_id, roles: :completable |
|
28 |
+ form_configurable :title |
|
29 |
+ |
|
30 |
+ def complete_list_id |
|
31 |
+ response = request_guard do |
|
32 |
+ HTTParty.get lists_url, request_options |
|
33 |
+ end |
|
34 |
+ response.map { |p| {text: "#{p['title']} (#{p['id']})", id: p['id']}} |
|
35 |
+ end |
|
36 |
+ |
|
37 |
+ def validate_options |
|
38 |
+ errors.add(:base, "you need to specify the list you want to add tasks to") unless options['list_id'].present? |
|
39 |
+ errors.add(:base, "you need to specify the title of the task to create") unless options['title'].present? |
|
40 |
+ end |
|
41 |
+ |
|
42 |
+ def working? |
|
43 |
+ !recent_error_logs? |
|
44 |
+ end |
|
45 |
+ |
|
46 |
+ def receive(incoming_events) |
|
47 |
+ incoming_events.each do |event| |
|
48 |
+ mo = interpolated(event) |
|
49 |
+ title = mo[:title][0..244] |
|
50 |
+ log("Creating new task '#{title}' on list #{mo[:list_id]}", inbound_event: event) |
|
51 |
+ request_guard do |
|
52 |
+ HTTParty.post tasks_url, request_options.merge(body: {title: title, list_id: mo[:list_id].to_i}.to_json) |
|
53 |
+ end |
|
54 |
+ end |
|
55 |
+ end |
|
56 |
+ private |
|
57 |
+ def request_guard(&blk) |
|
58 |
+ response = yield |
|
59 |
+ error("Error during http request: #{response.body}") if response.code > 400 |
|
60 |
+ response |
|
61 |
+ end |
|
62 |
+ |
|
63 |
+ def lists_url |
|
64 |
+ "https://a.wunderlist.com/api/v1/lists" |
|
65 |
+ end |
|
66 |
+ |
|
67 |
+ def tasks_url |
|
68 |
+ "https://a.wunderlist.com/api/v1/tasks" |
|
69 |
+ end |
|
70 |
+ |
|
71 |
+ def request_options |
|
72 |
+ {:headers => {'Content-Type' => 'application/json', |
|
73 |
+ 'User-Agent' => 'Huginn (https://github.com/cantino/huginn)', |
|
74 |
+ 'X-Access-Token' => service.token, |
|
75 |
+ 'X-Client-ID' => ENV["WUNDERLIST_OAUTH_KEY"] }} |
|
76 |
+ end |
|
77 |
+ end |
|
78 |
+end |
@@ -263,6 +263,12 @@ Devise.setup do |config| |
||
263 | 263 |
config.omniauth :dropbox, key, secret |
264 | 264 |
end |
265 | 265 |
|
266 |
+ if defined?(OmniAuth::Strategies::Wunderlist) && |
|
267 |
+ (key = ENV["WUNDERLIST_OAUTH_KEY"]).present? && |
|
268 |
+ (secret = ENV["WUNDERLIST_OAUTH_SECRET"]).present? |
|
269 |
+ config.omniauth :wunderlist, key, secret |
|
270 |
+ end |
|
271 |
+ |
|
266 | 272 |
# ==> Warden configuration |
267 | 273 |
# If you want to use other strategies, that are not supported by Devise, or |
268 | 274 |
# change the failure app, you can configure them inside the config.warden block. |
@@ -32,6 +32,7 @@ en: |
||
32 | 32 |
github: "GitHub" |
33 | 33 |
37signals: "37Signals (Basecamp)" |
34 | 34 |
dropbox: "Dropbox" |
35 |
+ wunderlist: 'Wunderlist' |
|
35 | 36 |
passwords: |
36 | 37 |
no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." |
37 | 38 |
send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." |
@@ -7,4 +7,5 @@ THIRTY_SEVEN_SIGNALS_OAUTH_KEY=TESTKEY |
||
7 | 7 |
THIRTY_SEVEN_SIGNALS_OAUTH_SECRET=TESTSECRET |
8 | 8 |
DROPBOX_OAUTH_KEY=dropboxoauthkey |
9 | 9 |
DROPBOX_OAUTH_SECRET=dropboxoauthsecret |
10 |
+WUNDERLIST_OAUTH_KEY=wunderoauthkey |
|
10 | 11 |
FAILED_JOBS_TO_KEEP=2 |
@@ -0,0 +1,74 @@ |
||
1 |
+require 'spec_helper' |
|
2 |
+require 'models/concerns/oauthable' |
|
3 |
+ |
|
4 |
+describe Agents::WunderlistAgent do |
|
5 |
+ it_behaves_like Oauthable |
|
6 |
+ |
|
7 |
+ before(:each) do |
|
8 |
+ |
|
9 |
+ @valid_params = { |
|
10 |
+ 'list_id' => '12345', |
|
11 |
+ 'title' => '{{title}}: {{url}}', |
|
12 |
+ } |
|
13 |
+ |
|
14 |
+ @checker = Agents::WunderlistAgent.new(:name => "somename", :options => @valid_params) |
|
15 |
+ @checker.user = users(:jane) |
|
16 |
+ @checker.service = services(:generic) |
|
17 |
+ @checker.save! |
|
18 |
+ |
|
19 |
+ @event = Event.new |
|
20 |
+ @event.agent = agents(:bob_weather_agent) |
|
21 |
+ @event.payload = { title: 'hello', url: 'www.example.com'} |
|
22 |
+ @event.save! |
|
23 |
+ end |
|
24 |
+ |
|
25 |
+ describe "validating" do |
|
26 |
+ before do |
|
27 |
+ expect(@checker).to be_valid |
|
28 |
+ end |
|
29 |
+ |
|
30 |
+ it "should require the title" do |
|
31 |
+ @checker.options['title'] = nil |
|
32 |
+ expect(@checker).not_to be_valid |
|
33 |
+ end |
|
34 |
+ |
|
35 |
+ it "should require the list_id" do |
|
36 |
+ @checker.options['list_id'] = nil |
|
37 |
+ expect(@checker).not_to be_valid |
|
38 |
+ end |
|
39 |
+ end |
|
40 |
+ |
|
41 |
+ it "should generate the request_options" do |
|
42 |
+ expect(@checker.send(:request_options)).to eq({:headers=>{"Content-Type"=>"application/json", "User-Agent"=>"Huginn (https://github.com/cantino/huginn)", "X-Access-Token"=>"1234token", "X-Client-ID"=>"wunderoauthkey"}}) |
|
43 |
+ end |
|
44 |
+ |
|
45 |
+ describe "#complete_list_id" do |
|
46 |
+ it "should return a array of hashes" do |
|
47 |
+ stub_request(:get, 'https://a.wunderlist.com/api/v1/lists').to_return( |
|
48 |
+ :body => JSON.dump([{title: 'test', id: 12345}]), |
|
49 |
+ :headers => {"Content-Type" => "text/json"} |
|
50 |
+ ) |
|
51 |
+ expect(@checker.complete_list_id).to eq([{:text=>"test (12345)", :id=>12345}]) |
|
52 |
+ end |
|
53 |
+ end |
|
54 |
+ |
|
55 |
+ describe "#receive" do |
|
56 |
+ it "send a message to the hipchat" do |
|
57 |
+ stub_request(:post, 'https://a.wunderlist.com/api/v1/tasks').with { |request| request.body == 'abc'} |
|
58 |
+ @checker.receive([@event]) |
|
59 |
+ end |
|
60 |
+ end |
|
61 |
+ |
|
62 |
+ describe "#working?" do |
|
63 |
+ it "should be working with no entry in the error log" do |
|
64 |
+ expect(@checker).to be_working |
|
65 |
+ end |
|
66 |
+ |
|
67 |
+ it "should not be working with a recent entry in the error log" do |
|
68 |
+ @checker.error("test") |
|
69 |
+ @checker.reload |
|
70 |
+ @checker.last_event_at = Time.now |
|
71 |
+ expect(@checker).to_not be_working |
|
72 |
+ end |
|
73 |
+ end |
|
74 |
+end |